package protocol

import (
	"net"
	"errors"
	"io"
	"encoding/binary"
	"log"
)

const (
	MaxPacketSize     = 1024 * 4 // 最大包为4k
	PacketHeaderSize = 4 // Size为uint16，占2字节
	PacketCommandSize = 4 // MainId(uint16) + SubId(uint16)
)


// 封包流
type PacketStream interface {
	Read() (*Packet, error)
	Write(pkt *Packet) error
	Close() error
	Raw() interface{}
}

type ltvStream struct {
	conn    net.Conn
}

var (
	packetTagNotMatch     = errors.New("ReadPacket: packet tag not match")
	packetDataSizeInvalid = errors.New("ReadPacket: packet crack, invalid size")
	packetTooBig          = errors.New("ReadPacket: packet too big")
)

//读取一个封包
func (ltv *ltvStream) Read() (p *Packet, err error) {
	//读取包头
	headData := make([]byte,PacketHeaderSize)
	if _, err = io.ReadFull(ltv.conn, headData); err != nil {
		return nil, err
	}
	// 用小端格式读取Size
	fullSize := binary.LittleEndian.Uint16(headData[PacketHeaderSize-2:PacketHeaderSize])
	dataSize := fullSize - PacketHeaderSize
	if dataSize < 0 {
		return nil, packetDataSizeInvalid
	}

	if dataSize > uint16(MaxPacketSize) {
		return nil, packetTooBig
	}

	//读取数据
	packData := make([]byte, dataSize)
	if _, err = io.ReadFull(ltv.conn, packData); err != nil {
		return nil, err
	}
	//效验接收到的大小
	if packData == nil || dataSize != uint16(len(packData)) {
		return nil, err
	}

	//封装成Packet
	p = &Packet{}
	p.MainId = uint16(binary.LittleEndian.Uint16(packData[0:2]))
	p.SubId = uint16(binary.LittleEndian.Uint16(packData[2:4]))
	p.Data = packData[4:]
	return p, nil
}

//封包发送给socket
func (ltv *ltvStream) Write(pkt *Packet) (err error) {
	userData := pkt.ToByteArray()
	//写MsgSize
	packetSize := len(userData) + PacketHeaderSize
	buffer := make([]byte, packetSize)
	//写入校验码
	binary.LittleEndian.PutUint16(buffer[0:2], uint16(100))
	//写入大小
	binary.LittleEndian.PutUint16(buffer[PacketHeaderSize-2:PacketHeaderSize], uint16(packetSize))
	//写数据
	buffer = append(buffer[:PacketHeaderSize], userData...)
	if _, err = ltv.conn.Write(buffer); err != nil {
		log.Println("写数据", err)
		return err
	}
	return nil
}

//关闭
func (ltv *ltvStream) Close() error {
	return ltv.conn.Close()
}

// 取原始连接
func (ltv *ltvStream) Raw() interface{} {
	if ltv.conn == nil {
		return nil
	}
	return ltv.conn
}

//封包流 relay模式: 在封包头有clientid信息
func NewPacketStream(conn net.Conn) PacketStream {
	return &ltvStream{
		conn:    conn,
	}
}
